# -*- coding: utf-8 -*-
"""
纯 SQL 仓库，无业务逻辑
"""
from __future__ import annotations

from typing import List, Dict, Any, Optional
from common.database import DatabaseManager


class AlarmRepository:
	def __init__(self, db: DatabaseManager):
		self.db = db

	def get_alarm_info_by_id(self, alarm_id: int) -> Optional[Dict[str, Any]]:
		"""获取告警的详细信息，包括rule_name"""
		rows = self.db.execute_query(
			"""
			SELECT a.id, a.meter_code, a.alarm_type, a.status, ar.rule_name
			FROM am_alarm a
			LEFT JOIN am_alarm_rule ar ON a.alarm_type = ar.rule_code
			WHERE a.id = %s
			""",
			(alarm_id,),
			commit=False
		)
		return rows[0] if rows else None

	def insert_alarm_work_order(self, alarm_id: int, wo_id: int) -> None:
		"""插入告警工单映射关系"""
		self.db.execute_query(
			"INSERT IGNORE INTO am_alarm_work(alarm_id, work_order_id) VALUES (%s,%s)",
			(alarm_id, wo_id),
			commit=False,
		)

	def find_open_alarm_types_by_meter(self, meter_code: str) -> List[str]:
		"""查找设备的所有开放告警类型"""
		rows = self.db.execute_query(
			"""
			SELECT DISTINCT alarm_type
			FROM am_alarm
			WHERE meter_code = %s AND status = 'OPEN'
			""",
			(meter_code,),
			commit=False
		)
		return [row["alarm_type"] for row in rows]

	def find_open_alarm_by_meter_and_type(self, meter_code: str, alarm_type: str) -> Optional[Dict[str, Any]]:
		"""查找指定设备和类型的开放告警（加锁）"""
		rows = self.db.execute_query(
			"""
			SELECT id, recover_token
			FROM am_alarm
			WHERE meter_code=%s AND alarm_type=%s AND status='OPEN'
			FOR UPDATE
			""",
			(meter_code, alarm_type),
			commit=False
		)
		return rows[0] if rows else None

	def find_open_alarms_with_lock(self, meter_code: str) -> List[Dict[str, Any]]:
		"""
		查找设备的所有开放告警（原子性操作，加锁）

		返回所有处于OPEN状态的告警记录，并对记录加行级锁防止并发修改。
		使用FOR UPDATE锁确保在事务期间告警状态不会被其他进程修改。

		Args:
			meter_code: 设备编码

		Returns:
			List[Dict]: 开放告警列表，每个元素包含id, alarm_type, recover_token, status
		"""
		rows = self.db.execute_query(
			"""
			SELECT id, alarm_type, recover_token, status, raised_at
			FROM am_alarm
			WHERE meter_code=%s AND status='OPEN'
			ORDER BY raised_at ASC
			FOR UPDATE
			""",
			(meter_code,),
			commit=False
		)
		return rows

	def find_alarm_info_with_lock(self, alarm_id: int) -> Optional[Dict[str, Any]]:
		"""
		获取告警信息并加锁（用于更新操作）

		Args:
			alarm_id: 告警ID

		Returns:
			Optional[Dict]: 告警信息或None
		"""
		rows = self.db.execute_query(
			"""
			SELECT id, meter_code, alarm_type, status, raised_at
			FROM am_alarm
			WHERE id=%s
			FOR UPDATE
			""",
			(alarm_id,),
			commit=False
		)
		return rows[0] if rows else None

	def delete_recovered_alarms_by_type(self, meter_code: str, alarm_type: str) -> int:
		"""
		删除同设备同类型的历史RECOVERED状态告警

		这是为了避免唯一键约束冲突，确保新的恢复状态能正常保存。

		Args:
			meter_code: 设备编码
			alarm_type: 告警类型

		Returns:
			int: 受影响的行数
		"""
		return self.db.execute_query(
			"""
			DELETE FROM am_alarm
			WHERE meter_code=%s AND alarm_type=%s AND status='RECOVERED'
			""",
			(meter_code, alarm_type),
			commit=False
		)

	def update_alarm_to_recovered(self, alarm_id: int, meter_code: str, alarm_type: str, recover_token: str) -> int:
		"""
		更新告警为RECOVERED状态

		使用严格的WHERE条件确保只更新符合条件的告警记录。

		Args:
			alarm_id: 告警ID
			meter_code: 设备编码
			alarm_type: 告警类型
			recover_token: 恢复令牌

		Returns:
			int: 受影响的行数
		"""
		return self.db.execute_query(
			"""
			UPDATE am_alarm
			SET status='RECOVERED', recovered_at=NOW(), recover_token=%s, close_reason='NORMAL'
			WHERE id=%s AND status='OPEN' AND meter_code=%s AND alarm_type=%s
			""",
			(recover_token, alarm_id, meter_code, alarm_type),
			commit=False
		)

	def update_alarm_recovered(self, alarm_id: int, recover_token: str) -> int:
		"""
		更新告警为恢复状态（高并发安全版本）

		这个方法采用三步骤操作确保高并发安全：
		1. 获取告警信息并加锁
		2. 清理历史RECOVERED状态记录
		3. 更新当前告警状态

		Args:
			alarm_id: 告警ID
			recover_token: 恢复令牌

		Returns:
			int: 受影响的行数（0表示更新失败）
		"""
		# 步骤1: 获取告警信息并加锁
		alarm_info = self.find_alarm_info_with_lock(alarm_id)
		if not alarm_info:
			return 0

		meter_code = alarm_info['meter_code']
		alarm_type = alarm_info['alarm_type']

		# 步骤2: 清理同设备同类型的历史RECOVERED状态告警
		self.delete_recovered_alarms_by_type(meter_code, alarm_type)

		# 步骤3: 更新当前告警为RECOVERED状态
		return self.update_alarm_to_recovered(alarm_id, meter_code, alarm_type, recover_token)

	def check_existing_alarm(self, msg_id: str, alarm_type: str) -> bool:
		"""检查是否已存在同类型告警（加锁）"""
		rows = self.db.execute_query(
			"""
			SELECT id FROM am_alarm
			WHERE msg_id = %s AND alarm_type = %s
			FOR UPDATE
			""",
			(msg_id, alarm_type),
			commit=False
		)
		return bool(rows)

	def insert_alarm(self, msg_id: str, meter_code: str, meter_type: str,
					 alarm_type: str, rule_snap: str, recover_token: str) -> int:
		"""插入新告警"""
		sql = """
            INSERT INTO am_alarm
              (msg_id, meter_code, meter_type, alarm_type, status, raised_at, rule_snap, recover_token)
            VALUES (%s,%s,%s,%s,'OPEN',NOW(),%s,%s)
            ON DUPLICATE KEY UPDATE id=LAST_INSERT_ID(id)
        """
		self.db.execute_query(
			sql,
			(msg_id, meter_code, meter_type, alarm_type, rule_snap, recover_token),
			commit=False,
		)
		return self.db.last_insert_id()
